﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Simplify
{
	public class VWLineSimplifier
	{
		public static Coordinate[] Simplify(Coordinate[] pts, double distanceTolerance)
		{
			return new VWLineSimplifier(pts, distanceTolerance).Simplify();
		}

		public VWLineSimplifier(Coordinate[] pts, double distanceTolerance)
		{
			this._pts = pts;
			this._tolerance = distanceTolerance * distanceTolerance;
		}

		public Coordinate[] Simplify()
		{
			PriorityQueueNode<VWLineSimplifier.IndexWithArea, int>[] array = new PriorityQueueNode<VWLineSimplifier.IndexWithArea, int>[this._pts.Length];
			AlternativePriorityQueue<VWLineSimplifier.IndexWithArea, int> alternativePriorityQueue = new AlternativePriorityQueue<VWLineSimplifier.IndexWithArea, int>(this._pts.Length);
			for (int i = 1; i < this._pts.Length - 1; i++)
			{
				double area = Triangle.Area(this._pts[i - 1], this._pts[i], this._pts[i + 1]);
				array[i] = new PriorityQueueNode<VWLineSimplifier.IndexWithArea, int>(i);
				alternativePriorityQueue.Enqueue(array[i], new VWLineSimplifier.IndexWithArea(i, area));
			}
			while (alternativePriorityQueue.Head != null && alternativePriorityQueue.Head.Priority.Area <= this._tolerance)
			{
				PriorityQueueNode<VWLineSimplifier.IndexWithArea, int> priorityQueueNode = alternativePriorityQueue.Dequeue();
				array[priorityQueueNode.Data] = null;
				int num = priorityQueueNode.Data - 1;
				while (num > 0 && array[num] == null)
				{
					num--;
				}
				int num2 = priorityQueueNode.Data + 1;
				while (num2 < this._pts.Length - 1 && array[num2] == null)
				{
					num2++;
				}
				if (num > 0 && alternativePriorityQueue.Contains(array[num]))
				{
					int num3 = num - 1;
					while (num3 > 0 && array[num3] == null)
					{
						num3--;
					}
					double area2 = Triangle.Area(this._pts[num3], this._pts[num], this._pts[num2]);
					alternativePriorityQueue.ChangePriority(array[num], new VWLineSimplifier.IndexWithArea(num, area2));
				}
				if (num2 < this._pts.Length - 1 && alternativePriorityQueue.Contains(array[num2]))
				{
					int num4 = num2 + 1;
					while (num4 < this._pts.Length - 1 && array[num4] == null)
					{
						num4++;
					}
					double area3 = Triangle.Area(this._pts[num], this._pts[num2], this._pts[num4]);
					alternativePriorityQueue.ChangePriority(array[num2], new VWLineSimplifier.IndexWithArea(num2, area3));
				}
			}
			CoordinateList coordinateList = new CoordinateList();
			coordinateList.Add(this._pts[0], false);
			for (int j = 0; j < array.Length; j++)
			{
				if (array[j] != null)
				{
					coordinateList.Add(this._pts[j], false);
				}
			}
			coordinateList.Add(this._pts[this._pts.Length - 1], coordinateList.Count == 1);
			return coordinateList.ToArray();
		}

		private readonly Coordinate[] _pts;

		private readonly double _tolerance;

		private struct IndexWithArea : IComparable<VWLineSimplifier.IndexWithArea>
		{
			public IndexWithArea(int index, double area)
			{
				this.index = index;
				this.area = area;
			}

			public double Area
			{
				get
				{
					return this.area;
				}
			}

			public int CompareTo(VWLineSimplifier.IndexWithArea other)
			{
				int num = this.area.CompareTo(other.area);
				if (num != 0)
				{
					return num;
				}
				return this.index.CompareTo(other.index);
			}

			private int index;

			private double area;
		}
	}
}
